#include <stdio.h>
#include <string.h>

#include "touch_test.h"


static void delay_us(__IO uint32_t delay)
{
    uint32_t i;

    for(i=0; i<delay; i++) {
        uint32_t uc = 12;

        while(uc--);
    }
}

/***************************************************************************************************
** Function name:           ILI9341_DrawCross
** Descriptions:            在ILI9341上显示校准触摸时需要的十字
** input parameters:        usX：在特定扫描方向下十字交叉点的x坐标
							usY：在特定扫描方向下十字交叉点的y坐标
** Returned value:          none
** Created by:              zheh.zhang
** Created date:            2021-04-24
**--------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
****************************************************************************************************/
static void ILI9341_DrawCross(uint16_t usX, uint16_t usY)
{
    ILI9341_DrawLine(usX-10, usY, usX+10, usY);
    ILI9341_DrawLine(usX, usY-10, usX, usY+10);
}

// uint8_t xpt2046_touch_calibrate(uint8_t lcd_mode) 
// {
//     uint8_t i;
//     char cStr[128];
//     uint16_t usTest_x = 0, usTest_y = 0, usGap_x = 0, usGap_y = 0;
//     char *pStr = 0;

//     strType_XPT2046_Coordinate strCrossCoordinate[4], strScreenSample[4];
//     strType_XPT2046_Coordinate CalibrationFactor;

//     LCD_SetFont(&Font8x16);
//     LCD_SetColors(BLUE, BLACK);

//     /* 设置扫描方向，横屏 */
//     ILI9341_GramScan(lcd_mode);

//     /* 设定“+”字交叉点的坐标 */
//     strCrossCoordinate[0].x = LCD_X_LENGTH >> 2;
//     strCrossCoordinate[0].y = LCD_Y_LENGTH >> 2;

//     strCrossCoordinate[1].x = strCrossCoordinate[0].x;
//     strCrossCoordinate[1].y = (LCD_Y_LENGTH * 3) >> 2;

//     strCrossCoordinate[2].x = (LCD_X_LENGTH * 3) >> 2;
//     strCrossCoordinate[2].y = strCrossCoordinate[1].y;

//     strCrossCoordinate[3].x = strCrossCoordinate[2].x;
//     strCrossCoordinate[3].y = strCrossCoordinate[0].y;


//     for(i=0; i<4; i++) {
//         ILI9341_Clear(0, 0, LCD_X_LENGTH, LCD_Y_LENGTH);

//         pStr = "Touch Calibrate ......";

//         /* 插入空格，居中显示 */
//         sprintf(cStr, "%*c%s", ((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-strlen(pStr))/2, ' ', pStr);
//         ILI9341_DispStringLine_EN(LCD_Y_LENGTH>>1, cStr);

//         /* 插入空格，居中显示 */
//         sprintf(cStr, "%*c%d", ((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-1)/2,' ',i+1);
//         ILI9341_DispStringLine_EN((LCD_Y_LENGTH>>1) - (((sFont *)LCD_GetFont())->height), cStr);

//         delay_us(300000);

//         ILI9341_DrawCross(strCrossCoordinate[i].x, strCrossCoordinate[i].y);

//         while(!xpt2046_ReadADC_Smooth_XY(&strScreenSample[i]));
//     }

//     return 1;
// }


static uint8_t XPT2046_Calculate_CalibrationFactor ( strType_XPT2046_Coordinate * pDisplayCoordinate, strType_XPT2046_Coordinate * pScreenSample, strType_XPT2046_Calibration * pCalibrationFactor )
{
	uint8_t ucRet = 1;

	
	/* K＝ ( X0－X2 )  ( Y1－Y2 )－ ( X1－X2 )  ( Y0－Y2 ) */
	pCalibrationFactor -> Divider =  ( ( pScreenSample [ 0 ] .x - pScreenSample [ 2 ] .x ) *  ( pScreenSample [ 1 ] .y - pScreenSample [ 2 ] .y ) ) - 
									                 ( ( pScreenSample [ 1 ] .x - pScreenSample [ 2 ] .x ) *  ( pScreenSample [ 0 ] .y - pScreenSample [ 2 ] .y ) ) ;
	
	
	if (  pCalibrationFactor -> Divider == 0  )
		ucRet = 0;

	else
	{
		/* A＝ (  ( XD0－XD2 )  ( Y1－Y2 )－ ( XD1－XD2 )  ( Y0－Y2 ) )／K	*/
		pCalibrationFactor -> An =  ( ( pDisplayCoordinate [ 0 ] .x - pDisplayCoordinate [ 2 ] .x ) *  ( pScreenSample [ 1 ] .y - pScreenSample [ 2 ] .y ) ) - 
								                ( ( pDisplayCoordinate [ 1 ] .x - pDisplayCoordinate [ 2 ] .x ) *  ( pScreenSample [ 0 ] .y - pScreenSample [ 2 ] .y ) );
		
		/* B＝ (  ( X0－X2 )  ( XD1－XD2 )－ ( XD0－XD2 )  ( X1－X2 ) )／K	*/
		pCalibrationFactor -> Bn =  ( ( pScreenSample [ 0 ] .x - pScreenSample [ 2 ] .x ) *  ( pDisplayCoordinate [ 1 ] .x - pDisplayCoordinate [ 2 ] .x ) ) - 
								                ( ( pDisplayCoordinate [ 0 ] .x - pDisplayCoordinate [ 2 ] .x ) *  ( pScreenSample [ 1 ] .x - pScreenSample [ 2 ] .x ) );
		
		/* C＝ ( Y0 ( X2XD1－X1XD2 )+Y1 ( X0XD2－X2XD0 )+Y2 ( X1XD0－X0XD1 ) )／K */
		pCalibrationFactor -> Cn =  ( pScreenSample [ 2 ] .x * pDisplayCoordinate [ 1 ] .x - pScreenSample [ 1 ] .x * pDisplayCoordinate [ 2 ] .x ) * pScreenSample [ 0 ] .y +
								                ( pScreenSample [ 0 ] .x * pDisplayCoordinate [ 2 ] .x - pScreenSample [ 2 ] .x * pDisplayCoordinate [ 0 ] .x ) * pScreenSample [ 1 ] .y +
								                ( pScreenSample [ 1 ] .x * pDisplayCoordinate [ 0 ] .x - pScreenSample [ 0 ] .x * pDisplayCoordinate [ 1 ] .x ) * pScreenSample [ 2 ] .y ;
		
		/* D＝ (  ( YD0－YD2 )  ( Y1－Y2 )－ ( YD1－YD2 )  ( Y0－Y2 ) )／K	*/
		pCalibrationFactor -> Dn =  ( ( pDisplayCoordinate [ 0 ] .y - pDisplayCoordinate [ 2 ] .y ) *  ( pScreenSample [ 1 ] .y - pScreenSample [ 2 ] .y ) ) - 
								                ( ( pDisplayCoordinate [ 1 ] .y - pDisplayCoordinate [ 2 ] .y ) *  ( pScreenSample [ 0 ] .y - pScreenSample [ 2 ] .y ) ) ;
		
		/* E＝ (  ( X0－X2 )  ( YD1－YD2 )－ ( YD0－YD2 )  ( X1－X2 ) )／K	*/
		pCalibrationFactor -> En =  ( ( pScreenSample [ 0 ] .x - pScreenSample [ 2 ] .x ) *  ( pDisplayCoordinate [ 1 ] .y - pDisplayCoordinate [ 2 ] .y ) ) - 
								                ( ( pDisplayCoordinate [ 0 ] .y - pDisplayCoordinate [ 2 ] .y ) *  ( pScreenSample [ 1 ] .x - pScreenSample [ 2 ] .x ) ) ;
		
		
		/* F＝ ( Y0 ( X2YD1－X1YD2 )+Y1 ( X0YD2－X2YD0 )+Y2 ( X1YD0－X0YD1 ) )／K */
		pCalibrationFactor -> Fn =  ( pScreenSample [ 2 ] .x * pDisplayCoordinate [ 1 ] .y - pScreenSample [ 1 ] .x * pDisplayCoordinate [ 2 ] .y ) * pScreenSample [ 0 ] .y +
								                ( pScreenSample [ 0 ] .x * pDisplayCoordinate [ 2 ] .y - pScreenSample [ 2 ] .x * pDisplayCoordinate [ 0 ] .y ) * pScreenSample [ 1 ] .y +
								                ( pScreenSample [ 1 ] .x * pDisplayCoordinate [ 0 ] .y - pScreenSample [ 0 ] .x * pDisplayCoordinate [ 1 ] .y ) * pScreenSample [ 2 ] .y;
			
	}
	
	
	return ucRet;
	
	
}


uint8_t XPT2046_Touch_Calibrate ( uint8_t LCD_Mode ) 
{

	uint8_t i;
		
	char cStr [ 100 ];
		
	uint16_t usTest_x = 0, usTest_y = 0, usGap_x = 0, usGap_y = 0;
		
	char * pStr = 0;
	
    strType_XPT2046_Coordinate strCrossCoordinate[4], strScreenSample[4];
	  
	strType_XPT2046_Calibration CalibrationFactor;
    		
    LCD_SetFont(&Font8x16);
    LCD_SetColors(BLUE,BLACK);

    //设置扫描方向，横屏
    ILI9341_GramScan ( LCD_Mode );
    
    
    /* 设定“十”字交叉点的坐标 */ 
    strCrossCoordinate [0].x = LCD_X_LENGTH >> 2;
    strCrossCoordinate[0].y = LCD_Y_LENGTH >> 2;
    
    strCrossCoordinate[1].x = strCrossCoordinate[0].x;
    strCrossCoordinate[1].y = ( LCD_Y_LENGTH * 3 ) >> 2;
    
    strCrossCoordinate[2].x = ( LCD_X_LENGTH * 3 ) >> 2;
    strCrossCoordinate[2].y = strCrossCoordinate[1].y;
    
    strCrossCoordinate[3].x = strCrossCoordinate[2].x;
    strCrossCoordinate[3].y = strCrossCoordinate[0].y;	
		
		
	for ( i = 0; i < 4; i ++ )
	{ 
		ILI9341_Clear ( 0, 0, LCD_X_LENGTH, LCD_Y_LENGTH );       
		
		pStr = "Touch Calibrate ......";		
		//插入空格，居中显示
		sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-strlen(pStr))/2,' ',pStr)	;	
		ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );			
	
		//插入空格，居中显示
		sprintf ( cStr, "%*c%d",((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width)) -1)/2,' ',i + 1 );
		ILI9341_DispStringLine_EN (( LCD_Y_LENGTH >> 1 ) - (((sFont *)LCD_GetFont())->height), cStr ); 
	
		delay_us(300000);		                     //适当的延时很有必要
		
		ILI9341_DrawCross ( strCrossCoordinate[i] .x, strCrossCoordinate[i].y );  //显示校正用的“十”字
		printf("strCrossCoordinate: %d, %d \n",strCrossCoordinate[i] .x, strCrossCoordinate[i].y);

		while ( ! xpt2046_ReadADC_Smooth_XY ( & strScreenSample [i] ) );               //读取XPT2046数据到变量pCoordinate，当ptr为空时表示没有触点被按下
		printf("strScreenSample: %d, %d \n",strScreenSample[i] .x, strScreenSample[i].y);
	}
		
		
	XPT2046_Calculate_CalibrationFactor ( strCrossCoordinate, strScreenSample, & CalibrationFactor ) ;  	 //用原始参数计算出 原始参数与坐标的转换系数
	
	if ( CalibrationFactor.Divider == 0 ) goto Failure;
	
		
	usTest_x = ( ( CalibrationFactor.An * strScreenSample[3].x ) + ( CalibrationFactor.Bn * strScreenSample[3].y ) + CalibrationFactor.Cn ) / CalibrationFactor.Divider;		//取一个点计算X值	 
	usTest_y = ( ( CalibrationFactor.Dn * strScreenSample[3].x ) + ( CalibrationFactor.En * strScreenSample[3].y ) + CalibrationFactor.Fn ) / CalibrationFactor.Divider;    //取一个点计算Y值
	
	usGap_x = ( usTest_x > strCrossCoordinate[3].x ) ? ( usTest_x - strCrossCoordinate[3].x ) : ( strCrossCoordinate[3].x - usTest_x );   //实际X坐标与计算坐标的绝对差
	usGap_y = ( usTest_y > strCrossCoordinate[3].y ) ? ( usTest_y - strCrossCoordinate[3].y ) : ( strCrossCoordinate[3].y - usTest_y );   //实际Y坐标与计算坐标的绝对差
	
    if ( ( usGap_x > 15 ) || ( usGap_y > 15 ) ) goto Failure;       //可以通过修改这两个值的大小来调整精度    
		

    /* 校准系数为全局变量 */ 
		strXPT2046_TouchPara[LCD_Mode].dx_x = ( CalibrationFactor.An * 1.0 ) / CalibrationFactor.Divider;
		strXPT2046_TouchPara[LCD_Mode].dx_y = ( CalibrationFactor.Bn * 1.0 ) / CalibrationFactor.Divider;
		strXPT2046_TouchPara[LCD_Mode].dx   = ( CalibrationFactor.Cn * 1.0 ) / CalibrationFactor.Divider;
		
		strXPT2046_TouchPara[LCD_Mode].dy_x = ( CalibrationFactor.Dn * 1.0 ) / CalibrationFactor.Divider;
		strXPT2046_TouchPara[LCD_Mode].dy_y = ( CalibrationFactor.En * 1.0 ) / CalibrationFactor.Divider;
		strXPT2046_TouchPara[LCD_Mode].dy   = ( CalibrationFactor.Fn * 1.0 ) / CalibrationFactor.Divider;
		
		#if 0	//输出调试信息，注意要先初始化串口
			{
					float * ulHeadAddres ;

					/* 打印校校准系数 */ 
					printf ( "显示模式【%d】校准系数如下：", LCD_Mode);
					
					ulHeadAddres = ( float* ) ( & strXPT2046_TouchPara[LCD_Mode] );
					
					for ( i = 0; i < 6; i ++ )
					{					
						printf ( "%12f,", *ulHeadAddres++  );			
					}	
					printf("\r\n");
			}
		#endif	
	
	ILI9341_Clear ( 0, 0, LCD_X_LENGTH, LCD_Y_LENGTH );
	
	LCD_SetTextColor(GREEN);
	
	pStr = "Calibrate Succed";
	//插入空格，居中显示	
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-strlen(pStr))/2,' ',pStr)	;	
  ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );	

  delay_us(300000);

	return 1;    
	

Failure:
	
	ILI9341_Clear ( 0, 0, LCD_X_LENGTH, LCD_Y_LENGTH ); 
	
	LCD_SetTextColor(RED);
	
	pStr = "Calibrate fail";	
	//插入空格，居中显示	
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-strlen(pStr))/2,' ',pStr)	;	
	ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );	

	pStr = "try again";
	//插入空格，居中显示		
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFont *)LCD_GetFont())->width))-strlen(pStr))/2,' ',pStr)	;	
	ILI9341_DispStringLine_EN ( ( LCD_Y_LENGTH >> 1 ) + (((sFont *)LCD_GetFont())->height), cStr );				

	delay_us(300000);		
	
	return 0; 
		
		
}


